# Author: itdby
# Date: 2024-05-30
# Description: A script to implement dynamic DNS resolution and automatically retrieve IP addresses.
# Version: V1.0

import requests
import json
import time
import hmac
import hashlib
import config
import logging

def get_ip_address(ip_addresses):
    for ip_address in ip_addresses:
        try:
            response = requests.get(ip_address, timeout=5)
            response.raise_for_status()
            return response.text
        except Exception as e:
            logging.warning(f"Error getting IP address from {ip_address}: {e}")
    return None

def update_dns_record(access_key_id, access_key_secret, region_id, domain_name, rr_name, ip_addresses):
    try:
        # Get the current public IP address
        current_ip = get_ip_address(ip_addresses)
        if current_ip is None:
            logging.error(f"Unable to get IP address for {domain_name} ({rr_name})")
            return

        # Get the current DNS record
        headers = {
            "Content-Type": "application/json"
        }
        request_url = f"https://alidns.{region_id}.aliyuncs.com/?Action=DescribeDomainRecords&DomainName={domain_name}&RRKeyWord={rr_name}"
        query_params = {
            "Format": "JSON",
            "Version": "2015-01-09",
            "AccessKeyId": access_key_id,
            "SignatureMethod": "HMAC-SHA1",
            "SignatureNonce": str(int(time.time())), 16),
            "SignatureVersion": "1.0",
            "Timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
        }
        signature = hmac.new(access_key_secret.encode("utf-8"), ("GET&" + "&" + request_url + "&" + "&".join(f"{k}{v}" for k, v in sorted(query_params.items()))).encode("utf-8")), hashlib.sha1).digest().hex()
        query_params["Signature"] = signature
        response = requests.get(request_url, params=query_params, headers=headers)
        response.raise_for_status()
        record = json.loads(response.text)["DomainRecords"]["Record"][0]

        # Check if the IP address has changed
        if record["Value"] != current_ip:
            # Update the DNS record
            data = {
                "RR": record["RR"],
                "Type": record["Type"],
                "Value": current_ip
            }
            request_url = f"https://alidns.{region_id}.aliyuncs.com/?Action=UpdateDomainRecord&RecordId={record['RecordId']}"
            query_params = {
                "Format": "JSON",
                "Version": "2015-01-09",
                "AccessKeyId": access_key_id,
                "SignatureMethod": "HMAC-SHA1",
                "SignatureNonce": str(int(time.time())), 16),
                "SignatureVersion": "1.0",
                "Timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
            }
            signature = hmac.new(access_key_secret.encode("utf-8"), ("POST&" + "&" + request_url + "&" + "&".join(f"{k}{v}" for k, v in sorted(query_params.items()))).encode("utf-8")), hashlib.sha1).digest().hex()
            query_params["Signature"] = signature
            response = requests.post(request_url, data=json.dumps(data), params=query_params, headers=headers)
            response.raise_for_status()
            logging.info(f"IP address updated to {current_ip} for {domain_name} ({rr_name})")
        else:
            logging.info(f"IP address has not changed: {current_ip} for {domain_name} ({rr_name})")
    except Exception as e:
        logging.error(f"Error updating DNS record for {domain_name} ({rr_name}): {e}")

def main():
    # Configure logging
    logging.basicConfig(filename="ddns.log", level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")

    # Alibaba Cloud API credentials
    ACCESS_KEY_ID = config.ACCESS_KEY_ID
    ACCESS_KEY_SECRET = config.ACCESS_KEY_SECRET
    REGION_ID = config.REGION_ID

    # List of domains and DNS records to update
    domains = config.DOMAINS

    # List of IP addresses to try
    ip_addresses = config.IP_ADDRESSES

    # Update DNS records for each domain
    for domain in domains:
        update_dns_record(ACCESS_KEY_ID, ACCESS_KEY_SECRET, REGION_ID, domain["domain_name"], domain["rr_name"], ip_addresses)

if __name__ == "__main__":
    main()
